#include "http_conn.h"
#include <mysql/mysql.h>
#include <fstream>//fstream类同时支持>>和<<操作符

/*定义HTTP响应的一些状态信息*/
const char* ok_200_title = "OK";

const char* error_400_title = "Bad Request";
const char* error_400_form = "Your request has bad syntax or is inherently impossible to satisfy.\n";//语法错误

const char* error_403_title = "Forbidden";
const char* error_403_form = "You do not have permission to get file from this server.\n";

const char* error_404_title = "Not found";
const char* error_404_form = "The requestes file was not found on this server.\n";

const char* error_500_title = "Interner Error";
const char* error_500_form = "There was an unusual problem serving the requestes file.\n";

locker m_lock;
std::map<std::string, std::string> users;
/*网站的根目录*/
//const char* doc_root = "/var/www/html";

void http_conn::initmysql_result( connection_pool* connPool){
    //先从连接池中取一个连接
    MYSQL* mysql = NULL;
    connectionRAII mysqlcon( &mysql, connPool );

    //在user表中检索username，passwd数据，浏览器端输入
    if( mysql_query( mysql, "SELECT username,password FROM user" ) ){
        LOG_ERROR( "SELECT error: %s\n", mysql_error( mysql ) );
    }

    //从表中检索完整的结果集
    //把查询的数据从服务器端取到客户端，然后缓存起来，放在句柄mysql里面
    MYSQL_RES *result = mysql_store_result(mysql);

    //返回结果集中的列数
    int num_fields = mysql_num_fields(result);

    //返回所有字段结构的数组
    MYSQL_FIELD *fields = mysql_fetch_fields(result);

    //从结果集中获取下一行，将对应的用户名和密码，存入map中
    while (MYSQL_ROW row = mysql_fetch_row(result))
    {
        std::string temp1(row[0]);
        std::string temp2(row[1]);
        users[temp1] = temp2;
    }


}

int setnonblocking( int fd ){
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

void addfd( int epollfd, int fd, bool one_shot , int TRIGMode ){
    epoll_event event;
    event.data.fd = fd;
    if( 1 == TRIGMode ){
        //ET模式
        event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;//EPOLLRDHUP表示读关闭
    }else{
        //LT模式
        event.events = EPOLLIN | EPOLLRDHUP;
    }
    if( one_shot ){
        event.events |= EPOLLONESHOT;//操作系统最多触发一个注册的可读，可写，异常事件，且只能触发一次
    }
    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking( fd );
}
 
void removefd( int epollfd, int fd ){
    epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 );
    close( fd );
}

void modfd( int epollfd, int fd, int ev , int TRIGMode){
    epoll_event event;
    event.data.fd = fd;
    if( 1 == TRIGMode ){
        //ET模式
        event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;//EPOLLRDHUP表示读关闭
    }else{
        //LT模式
        event.events = ev | EPOLLONESHOT | EPOLLRDHUP;
    }
    epoll_ctl( epollfd, EPOLL_CTL_MOD, fd, &event);
}

int http_conn::m_user_count = 0;
int http_conn::m_epollfd = -1;

void http_conn::close_conn(bool real_close){
    if( real_close && ( m_sockfd != -1 ) ){
        //printf("close %d\n", m_sockfd);
        removefd( m_epollfd,m_sockfd );
        m_sockfd = -1;
        m_user_count--;
    }
}

void http_conn::init( int sockfd, const sockaddr_in& addr, char *root, int TRIGMode,
                     int close_log, std::string user, std::string passwd, std::string sqlname ){
    m_sockfd = sockfd;
    m_address = addr;
    /*如下两行是避免time_wait状态，仅用于调试
    int reuse = 1;
    setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) );*/
    addfd( m_epollfd, sockfd, true , m_TRIGMode );
    m_user_count++;

    //当浏览器出现连接重置时，可能是网站根目录出错或http响应格式出错或者访问的文件中内容完全为空
    doc_root = root;
    m_TRIGMode = TRIGMode;
    m_close_log = close_log;

    strcpy(sql_user, user.c_str());//c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同
    strcpy(sql_passwd, passwd.c_str());
    strcpy(sql_name, sqlname.c_str());

    init();
}

//初始化新接受的连接
//check_state默认为分析请求行状态
void http_conn::init(){
    m_check_state = CHECK_STATE_REQUESTLINE;
    m_linger = false;

    m_method = GET;
    m_url = 0;
    m_version = 0;
    m_content_length = 0;
    m_host = 0;
    m_start_line = 0;
    m_checked_idx = 0;
    m_read_idx = 0;
    m_write_idx = 0;

    mysql = NULL;
    bytes_to_send = 0;
    bytes_have_send = 0;
    cgi = 0;
    m_state = 0;
    timer_flag = 0;
    improv = 0;

    memset( m_read_buf, '\0', READ_BUFFER_SIZE );
    memset( m_write_buf, '\0', WRITE_BUFFER_SIZE );
    memset( m_real_file, '\0', FILENAME_LEN );
}
/*从状态机*/
http_conn::LINE_STATUS http_conn::parse_line(){
    char temp;
    for( ; m_checked_idx < m_read_idx; m_checked_idx++ ){
        temp = m_read_buf[ m_checked_idx ];
        //\r 是回车符
        //\n 是换行符
        if( temp == '\r' ){
            if( m_checked_idx + 1 == m_read_idx ){
                return LINE_OPEN;
            }else if( m_read_buf[ m_checked_idx + 1 ] == '\n' ){
                //设置buff字符串的结束标记\0
                m_read_buf[ m_checked_idx++ ] = '\0';//\r 换成结束符
                m_read_buf[ m_checked_idx++ ] = '\0';//\n 换成结束符
                //m_checked_index现在指向 \n下一位
                return LINE_OK;
            }
            return LINE_BAD;
        }else if( temp == '\n' ){
            if( m_checked_idx > 1 && m_read_buf[ m_checked_idx - 1 ] == '\r' ){
                m_read_buf[ m_checked_idx-1 ] = '\0';//\r 换成结束符
                m_read_buf[ m_checked_idx++ ] = '\0';//\n 换成结束符
                //m_checked_index现在指向 \n下一位
                return LINE_OK;
            }
            return LINE_BAD;
        }
    }
    /*如果内存中的内容都分析完毕，没有遇到\r，就表示还要继续读取数据*/
    return LINE_OPEN;
}
/*循环读取客户数据，直到无数据或者连接关闭
循环读取客户数据，直到无数据可读或对方关闭连接
非阻塞ET工作模式下，需要一次性将数据读完*/
bool http_conn::read_once(){
    if( m_checked_idx >= READ_BUFFER_SIZE ){
        return false;
    }
    int bytes_read = 0;
    //LT
    if( m_TRIGMode == 0 ){
        bytes_read = recv( m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0 );
        m_read_idx += bytes_read;
        if( bytes_read <= 0 ){
            return false;
        }
        return true;
    }
    //ET
    else{
        while( true ){
            bytes_read = recv( m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0 );
            if( bytes_read == -1 ){
                //在VxWorks和Windows上，EAGAIN的名字叫做EWOULDBLOCK。
                if( errno == EAGAIN || errno == EWOULDBLOCK ){
                    break;
                }
                return false;
            }else if( bytes_read == 0 ){
                return false;
            }
            m_read_idx += bytes_read;
        }
        return true;
    }
    
}
/*分析HTTP请求行,获取请求方法，目标url,以及http的版本号*/
/* "GET http://www.service.com/(video) HTTP/1.1" */
http_conn::HTTP_CODE http_conn::parse_request_line( char* text ){
    m_url = strpbrk( text, " \t");//找到第一个匹配的字符：空格
    if( !m_url ){
        return BAD_REQUEST;
    }
    *m_url = '\0';
    m_url++;

    char* method = text;
    //strcasecmp用忽略大小写比较字符串
    if( strcasecmp( method, "GET" ) == 0 ){
        m_method = GET;
    }
    else if( strcasecmp( method, "POST" ) == 0 ){
        m_method = POST;
        cgi = 1;
    }
    else{
        return BAD_REQUEST;
    }

    //m_url此时跳过了第一个空格或\t字符，但不知道之后是否还有
    //所以要再次检查
    m_url += strspn(m_url," \t");//strspn(s1,s2)返回s1中第一个不在s2中的字符下标，所以现在*url指向http的h
    m_version = strpbrk( m_url, " \t");
    if( !m_version ){
        return BAD_REQUEST;
    }

    *m_version++ = '\0';
    m_version+=strspn(m_version," \t");
    //仅支持HTTP/1.1
    if( strcasecmp( m_version, "HTTP/1.1" ) != 0 ){
        return BAD_REQUEST;
    }

    //对请求资源前7个字符进行判断
    //这里主要是有些报文的请求资源中会带有http://，这里需要对这种情况进行单独处理
    if( strncasecmp( m_url, "http://", 7 ) == 0 ){
        m_url += 7;
        m_url = strchr(m_url,'/');//char *strchr(const char *str, int c)返回在字符串 str 中第一次出现字符 c 的位置
    }

    //同样增加https情况
   if(strncasecmp( m_url,"https://", 8 )==0){
        m_url+=8;
        m_url=strchr(m_url,'/');
    }
    //一般的不会带有上述两种符号，直接是单独的/或/后面带访问资源
    if( !m_url || m_url[ 0 ] != '/' ){
        return BAD_REQUEST;
    }

    //当url为/时，显示判断界面
    if (strlen(m_url) == 1)
        strcat(m_url, "judge.html");
    
    m_check_state = CHECK_STATE_HEADER;

    return NO_REQUSET;//只分析了请求行，还没结束
}

/*解析http头部信息*/
/*
GET http://www.baidu.com/(video) HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36
Sec-Fetch-Dest: document
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: BIDUPSID=ABC012398147;ZD_ENTRY=baidu
*/
http_conn::HTTP_CODE http_conn::parse_headers( char* text ){
    //遇到空行，表示头部字段解释完毕
    if( text[ 0 ] == '\0' ){
        //如果HTTP的请求有消息体，则还要读取m_content_length字节的消息体，状态机转移到CHECK_STATE_CONTENT
        if( m_content_length != 0 ){
            m_check_state = CHECK_STATE_CONTENT;
            return NO_REQUSET;
        }
        return GET_REQUEST;
    }
    //处理connection
    else if( strncasecmp( text, "Connection:", 11 ) == 0 ){
        text += 11;
        text += strspn( text, " \t" );
        if( strcasecmp( text, "Keep-alive" ) == 0 ){
            //如果是长连接，则将linger标志设置为true
            m_linger = true;
        }
    }
    //处理connection——length
    else if( strncasecmp( text, "Content-Length:", 15 ) == 0 ){
        text += 15;
        text += strspn( text, " \t" );
        m_content_length = atol( text );//用来将字符串转换成长整型数

    }
    //处理host字段
    else if( strncasecmp( text, "Host:", 5 ) == 0 ){
        text += 5;
        text += strspn( text, " \t" );
        m_host = text;

    }else{
        printf("opp! unknow header %s\n", text );
    }
    
    return NO_REQUSET;  
}

/*我们没有真正的解析http请求的消息体，只是判断是否完整读入*/
http_conn::HTTP_CODE http_conn::parse_content( char* text ){
    if( m_read_idx >= (m_content_length + m_checked_idx ) ){
        text[ m_content_length ] = '\0';

        //POST请求中最后为输入的用户名和密码
        m_string = text;
        return GET_REQUEST;
    }
    return NO_REQUSET;
}

/*主状态机*/
http_conn::HTTP_CODE http_conn::process_read(){
    LINE_STATUS line_status = LINE_OK;
    HTTP_CODE ret = NO_REQUSET;
    char* text = 0;
    /*
    初始状态:m_check_state = CHECK_STATE_REQUESTLINE,line_status = LINE_OK;
            调用parse_request_line( text )结果ret = NO_REQUSET
            m_check_state 转换状态为CHECK_STATE_HEADER
    第二状态：m_check_state = CHECK_STATE_HEADER,line_status = LINE_OK；
            调用parse_headers( text )分析头部字段，在主状态机内循环读取头部字段
            读取到空行时表示头部字段解释完毕，状态发生变化
            m_content_length != 0 m_check_state = CHECK_STATE_CONTENT
            m_content_length == 0 ret = GET_REQUEST
    第三状态：m_check_state = CHECK_STATE_CONTENT，由于上一次的line_status == LINE_OK，所以不再读取行
            调用parse_content( char* text )读取消息体
            消息体解析后，将line_status变量更改为LINE_OPEN，主状态机的状态还是CHECK_STATE_CONTENT
            避免再次进入循环
    */
    while( ( ( m_check_state == CHECK_STATE_CONTENT ) && ( line_status == LINE_OK ) ) || ( ( line_status = parse_line() ) == LINE_OK ) ){
        text = get_line();//m_read_buf + m_start_line
        m_start_line = m_checked_idx;//m_checked_idx在parse_line()中
        printf( " Got 1 http line : %s \n" , text );

        switch ( m_check_state )
        {
            case CHECK_STATE_REQUESTLINE:
            {  
                ret = parse_request_line( text );
                if( ret == BAD_REQUEST ){
                    return BAD_REQUEST;
                }
                break;
            }
            case CHECK_STATE_HEADER:
            {
                ret = parse_headers( text );
                if( ret == BAD_REQUEST ){
                    return BAD_REQUEST;
                }else if( ret == GET_REQUEST ){
                    return do_request();
                }
                break;
            }
            case CHECK_STATE_CONTENT:
            {
                ret = parse_content( text );

                if( ret == GET_REQUEST ){
                    return do_request();
                }
                line_status = LINE_OPEN;
                break;
            }
            default:
                return INTERNAL_ERROR;
        }
    }
    return NO_REQUSET;
}

/*
m_url为请求报文中解析出的请求资源，以/开头，也就是/xxx，项目中解析后的m_url有8种情况
(1) /：GET请求，跳转到judge.html，即欢迎访问页面
(2) /0：POST请求，跳转到register.html，即注册页面
(3) /1：POST请求，跳转到log.html，即登录页面
(4) /2CGISQL.cgi：
        POST请求，进行登录校验
        验证成功跳转到welcome.html，即资源请求成功页面
        验证失败跳转到logError.html，即登录失败页面
(5) /3CGISQL.cgi：
        POST请求，进行注册校验
        注册成功跳转到log.html，即登录页面
        注册失败跳转到registerError.html，即注册失败页面
(6) /5：POST请求，跳转到picture.html，即图片请求页面
(7) /6：POST请求，跳转到video.html，即视频请求页面
(8) /7：POST请求，跳转到fans.html，即关注页面
*/
http_conn::HTTP_CODE http_conn::do_request(){

    strcpy( m_real_file, doc_root );//char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest
    int len = strlen( doc_root );
    //char *strrchr(const char *str, int c) 在参数 str 所指向的字符串中搜索最后一次出现字符 c（一个无符号字符）的位置。
    const char* p = strrchr(m_url, '/');
    //printf("m_url:%s\n", m_url);
    
    //处理cgi
    if ( cgi == 1 && (*(p + 1) == '2' || *(p + 1) == '3') ){
        char flag = m_url[1];
        char *m_url_real = new char[200];
        strcpy(m_url_real, "/");
        strcat(m_url_real, m_url + 2);
        strncpy(m_real_file + len, m_url_real, FILENAME_LEN - len - 1);
        delete [] m_url_real;

        //将用户名和密码提取出来
        //user=123&password=123
        char name[100], password[100];
        int i;
        for (i = 5; m_string[i] != '&'; ++i){
            name[i - 5] = m_string[i];
        }
        name[i - 5] = '\0';

        int j = 0;
        for (i = i + 10; m_string[i] != '\0'; ++i, ++j){
            password[j] = m_string[i];
        }
        password[j] = '\0';
        //注册
        if( *( p + 1 ) == '3' ){
            //先检测数据库中是否有重名的，没有重名的，进行增加数据
            //INSERT INTO user(username, passwd) VALUES('name', 'password')
            char *sql_new = new char[200];
            strcpy(sql_new, "INSERT INTO user(username, password) VALUES(");
            strcat(sql_new, "'");
            strcat(sql_new, name);
            strcat(sql_new, "', '");
            strcat(sql_new, password);
            strcat(sql_new, "')");

            if( users.find(name) == users.end() ){
                m_lock.lock();
                int res = mysql_query( mysql, sql_new );
                users.insert(pair<string, string>(name, password));
                m_lock.unlock();

                if( !res ){
                    //校验成功，跳转登录页面
                    strcpy(m_url, "/log.html");
                }else{
                    //校验失败，跳转注册失败页面
                    strcpy(m_url, "/registerError.html");
                }
            }else{
                strcpy(m_url, "/registerError.html");
            }
            delete [] sql_new;
        }
        //若浏览器端输入的用户名和密码在表中可以查找到，返回1，否则返回0
        else if( *( p + 1 ) == '2' ){
            if (users.find(name) != users.end() && users[name] == password){
                strcpy(m_url, "/welcome.html");
            }
            else{
                strcpy(m_url, "/logError.html");
            }
        }
    }
    if( *(p + 1) == '0' ){
        char *m_url_real = new char[200];
        strcpy( m_url_real,"/register.html");
        /*char *strncpy(char *dest, const char *src, size_t n) 
        把 src 所指向的字符串复制到 dest，最多复制 n 个字符。当 src 的长度小于 n 时，dest 的剩余部分将用空字节填充*/
        strncpy(m_real_file+ len, m_url_real,strlen(m_url_real) );
        delete [] m_url_real;
    }
    else if( *(p + 1) == '1' ){
        char *m_url_real = new char[200];
        strcpy( m_url_real,"/log.html");
        /*char *strncpy(char *dest, const char *src, size_t n) 
        把 src 所指向的字符串复制到 dest，最多复制 n 个字符。当 src 的长度小于 n 时，dest 的剩余部分将用空字节填充*/
        strncpy(m_real_file+ len, m_url_real,strlen(m_url_real) );
        delete [] m_url_real;
    }
    else if( *(p + 1) == '5' ){
        char *m_url_real = new char[200];
        strcpy( m_url_real,"/first.html");
        strncpy(m_real_file+ len, m_url_real,strlen(m_url_real) );
        delete [] m_url_real;
    }
    else if( *(p + 1) == '6' ){
        char *m_url_real = new char[200];
        strcpy( m_url_real,"/second.html");
        strncpy(m_real_file+ len, m_url_real,strlen(m_url_real) );
        delete [] m_url_real;
    }
    else if( *(p + 1) == '7' ){
        char *m_url_real = new char[200];
        strcpy( m_url_real,"/third.html");
        strncpy(m_real_file+ len, m_url_real,strlen(m_url_real) );
        delete [] m_url_real;
    }
    else{
        strncpy( m_real_file + len, m_url, FILENAME_LEN - len - 1);
    }
    /*
    int stat(const char * file_name, struct stat *buf);
    stat()用来将参数file_name 所指的文件类型和权限,文件大小，字节数
    复制到参数buf 所指的结构中
    */
    if( stat( m_real_file, &m_file_stat ) < 0 ){
        return NO_RESOURCE;
    }
    //st_mode文件的类型和存取的权限 
    //S_IROTH读权限
    if( !( m_file_stat.st_mode & S_IROTH ) ){
        return FORBIDDEN_REQUEST;
    }
    //S_ISDIR ()函数的作用是判断一个路径是不是目录
    if( S_ISDIR( m_file_stat.st_mode ) ){
        return BAD_REQUEST;
    }
    //以只读方式获取文件描述符，通过mmap将该文件映射到内存中
    int fd = open( m_real_file, O_RDONLY );
    //共享内存

    m_file_address = ( char* )mmap( 0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );

    close ( fd );
    //表示请求文件存在，且可以访问
    return FILE_REQUEST;
}

/*对内存映射区域执行munmap操作*/
void http_conn::unmap(){
    if( m_file_address ){
        munmap( m_file_address, m_file_stat.st_size );
        m_file_address = 0;
    }
}

/* 写HTTP的响应 */
bool http_conn::write(){

    int temp = 0;

    //发送的数据长度为0,表示响应报文为空，一般不会出现这种情况
    if( bytes_to_send == 0 ){
        modfd( m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode );
        init();
        return true;
    }
    while( 1 ){
        //writev以顺序m_iov[0]，m_iov[1]至m_iov[iovcnt-1]从缓冲区中聚集输出数据
        temp = writev( m_sockfd, m_iv, m_iv_count );
        if( temp <= -1 ){
            /*
            EAGAIN代表tcp写缓冲没有空间，重新注册EPOLLOUT，则等待下一轮的EPOLLOUT事件（写缓冲区从不可写变为可写，会触发）
            虽然在此期间，服务器无法接收同一客户的下一个请求，但是可以保证连接的完整性

            若不是缓冲区满了而失败，则取消mmap映射，关闭连接
            */
           if( errno == EAGAIN ){
               modfd( m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode );
               return true;
           }
           unmap();
           return false;
        }

        bytes_to_send -= temp;
        bytes_have_send += temp;
        //m_iv[0]信息的数据已发送完
        if (bytes_have_send >= m_iv[0].iov_len)
        {
            m_iv[0].iov_len = 0;
            //请求文件的 初始地址 + 已经发送的文件字节数
            m_iv[1].iov_base = m_file_address + ( bytes_have_send - m_write_idx );
            m_iv[1].iov_len = bytes_to_send;
        }
        else
        {
            m_iv[0].iov_base = m_write_buf + bytes_have_send;
            m_iv[0].iov_len = m_iv[0].iov_len - bytes_have_send;
        }

        //判断条件，数据已全部发送完
        //长连接重置http类实例，注册读事件，不关闭连接，
        //短连接直接关闭连接
        if( bytes_to_send <= 0 ){
            /*发送http响应成功，根据HTTP请求中的connection字段决定是否立即关闭连接*/
            unmap();
            modfd( m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode );
            if( m_linger ){
                init();
                return true;
            }else{
                return false;
            }
        }
    }
}
/*往写缓冲区中写入待发送的数据*/
bool http_conn::add_response( const char* format, ... ){
    if( m_write_idx >= WRITE_BUFFER_SIZE ){
        return false;
    }
    va_list arg_list;
    va_start( arg_list, format );
 
    /*
    int vsnprintf (char * sbuf, size_t n, const char * format, va_list arg );
    参数sbuf：用于缓存格式化字符串结果的字符数组

    参数n：限定最多打印到缓冲区sbuf的字符的个数为n-1个，因为vsnprintf还要在结果的末尾追加\0。如果格式化字符串长度大于n-1，则多出的部分被丢弃。如果格式化字符串长度小于等于n-1，则可以格式化的字符串完整打印到缓冲区sbuf。一般这里传递的值就是sbuf缓冲区的长度。

    参数format：格式化限定字符串

    参数arg：可变长度参数列表

    返回：成功打印到sbuf中的字符的个数，不包括末尾追加的\0。如果格式化解析失败，则返回负数*/
    int len = vsnprintf( m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list );
    if( len >= ( WRITE_BUFFER_SIZE - 1 -m_write_idx ) ){
        va_end( arg_list );
        return false;
    }
    //更新m_write_idx位置
    m_write_idx += len;
    va_end( arg_list );

    LOG_INFO("request:%s", m_write_buf);

    return true;
}

bool http_conn::add_status_line( int status, const char* title ){
    return add_response( "%s %d %s\r\n", "HTTP/1.1", status, title );
} 

bool http_conn::add_headers( int content_len ){

    return add_content_length( content_len ) && add_linger() && add_blank_line();

}

bool http_conn::add_content_length( int content_len ){
    return add_response("Content-Length: %d\r\n", content_len );
}

bool http_conn::add_linger(){

    return add_response("Connection: %s\r\n", ( m_linger == true ) ? "keep-alive" : "close" );

}

bool http_conn::add_blank_line(){

    return add_response("%s","\r\n");

}

bool http_conn::add_content( const char* content ){

    return add_response( "%s", content );

}

/*根据服务器处理http的请求结果，决定返回给客户端的内容*/
bool http_conn::process_write( HTTP_CODE ret ){
    switch( ret ){
        case INTERNAL_ERROR:
        {
            add_status_line( 500, error_500_title );
            add_headers( strlen( error_500_form ) );
            if( !add_content( error_500_form ) ){
                return false;
            }
            break;
        }
        case BAD_REQUEST:
        {
            add_status_line( 400, error_400_title );
            add_headers( strlen( error_400_form ) );
            if( !add_content( error_400_form ) ){
                return false;
            }
            break;
        }
        case NO_RESOURCE:
        {
            add_status_line( 404, error_404_title );
            add_headers( strlen( error_404_form ) );
            if( !add_content( error_404_form ) ){
                return false;
            }
            break;
        }
        case FORBIDDEN_REQUEST:
        {
            add_status_line( 403, error_403_title );
            add_headers( strlen( error_403_form ) );
            if( !add_content( error_403_form ) ){
                return false;
            }
            break;
        }
        case FILE_REQUEST:
        {
            add_status_line( 200, ok_200_title );
            if( m_file_stat.st_size != 0 ){
                add_headers( m_file_stat.st_size );
                m_iv[ 0 ].iov_base = m_write_buf;
                m_iv[ 0 ].iov_len = m_write_idx;
                m_iv[ 1 ].iov_base = m_file_address;
                m_iv[ 1 ].iov_len = m_file_stat.st_size;
                m_iv_count = 2;
                bytes_to_send = m_write_idx + m_file_stat.st_size;
                return true;
            }else{
                const char* ok_string = "<html><body></body/></hmtl>";
                add_headers( strlen( ok_string ) );
                if( !add_content( ok_string ) ){
                    return false;
                }
            }
        }
        default:{
            return false;
        }
    }
    m_iv[ 0 ].iov_base = m_write_buf;
    m_iv[ 0 ].iov_len = m_write_idx;
    m_iv_count = 1;
    bytes_to_send = m_write_idx;
    return true;
}

/*线程池中的工作调用线程*/
void http_conn::process(){
    HTTP_CODE read_ret = process_read();
    if( read_ret == NO_REQUSET ){
        modfd( m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode );
        return;
    }

    bool write_ret = process_write( read_ret );
    if( !write_ret ){
        close_conn();
    }
    modfd( m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode );
}